import { Injectable, Logger } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, DataSource } from 'typeorm';
import * as puppeteer from 'puppeteer';
import { Novel } from '../entities/novel.entity';
import { NovelChapter } from '../entities/novel-chapter.entity';
import { MasterCategory } from '../entities/master-category.entity';
import { CategoryMappingService } from './category-mapping.service';
import { TextUtils } from '../utils/text-utils';
import { PageUtils } from '../utils/page-utils';
import {
  ParsedNovelData,
  ParsedChapterData,
  CrawlerConfig,
  NovelCrawlResult,
  CrawlerProgress,
  CrawlerStatus,
} from '../types/crawler.types';

@Injectable()
export class QidianCrawlerV5 {
  private readonly logger = new Logger(QidianCrawlerV5.name);
  private browser: puppeteer.Browser | null = null;

  // 配置选项
  private readonly config: CrawlerConfig = {
    maxContentChapters: 10, // 最多获取章节内容的数量
    batchSize: 3, // 批量获取章节时的批次大小
    delayBetweenRequests: 1500, // 请求间隔（毫秒）
    maxRetries: 3, // 最大重试次数
    userAgent:
      'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
    timeout: 30000,
  };

  // User-Agent轮换池
  private readonly userAgents = [
    'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
    'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36',
    'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/120.0',
    'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/119.0',
    'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
    'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.1 Safari/605.1.15',
    'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0',
  ];

  // 爬虫状态
  private progress: CrawlerProgress = {
    status: 'idle',
    currentChapter: 0,
    totalChapters: 0,
    progress: 0,
    currentOperation: '等待中...',
  };

  constructor(
    @InjectRepository(Novel)
    private readonly novelRepository: Repository<Novel>,
    @InjectRepository(NovelChapter)
    private readonly chapterRepository: Repository<NovelChapter>,
    @InjectRepository(MasterCategory)
    private readonly masterCategoryRepository: Repository<MasterCategory>,
    private readonly dataSource: DataSource,
    private readonly categoryMappingService: CategoryMappingService,
  ) {}

  /**
   * 随机获取User-Agent
   */
  private getRandomUserAgent(): string {
    return this.userAgents[Math.floor(Math.random() * this.userAgents.length)];
  }

  /**
   * 随机延迟函数，避免被检测为机器人
   */
  private async randomDelay(
    minMs: number = 2000,
    maxMs: number = 5000,
  ): Promise<void> {
    const delay = Math.floor(Math.random() * (maxMs - minMs + 1)) + minMs;
    await new Promise((resolve) => setTimeout(resolve, delay));
  }

  async initBrowser(): Promise<void> {
    if (!this.browser) {
      const executablePath =
        process.env.CHROME_EXECUTABLE_PATH ||
        'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe';
      this.browser = await puppeteer.launch({
        headless: true,
        executablePath,
        args: [
          '--no-sandbox',
          '--disable-setuid-sandbox',
          '--disable-dev-shm-usage',
          '--disable-gpu',
          '--remote-debugging-port=9223',
          '--disable-blink-features=AutomationControlled', // 禁用自动化控制标识
          '--disable-features=VizDisplayCompositor',
          '--disable-web-security',
          '--disable-features=IsolateOrigins,site-per-process',
          '--disable-background-timer-throttling',
          '--disable-backgrounding-occluded-windows',
          '--disable-renderer-backgrounding',
          '--disable-background-networking',
          '--disable-default-apps',
          '--disable-extensions',
          '--disable-sync',
          '--disable-translate',
          '--hide-scrollbars',
          '--metrics-recording-only',
          '--mute-audio',
          '--no-first-run',
          '--safebrowsing-disable-auto-update',
          '--disable-infobars',
          '--disable-notifications',
          '--disable-popup-blocking',
          '--ignore-certificate-errors',
          '--ignore-certificate-errors-spki-list',
          '--ignore-ssl-errors',
          '--ignore-certificate-errors-spki-list',
        ],
      });
    }
  }

  async closeBrowser(): Promise<void> {
    if (this.browser) {
      await this.browser.close();
      this.browser = null;
    }
  }

  /**
   * 仅解析小说基础信息（不包含章节内容）- 快速同步方法
   */
  async parseBasicInfoOnly(url: string): Promise<any> {
    const startTime = Date.now();
    this.logger.log(`开始解析起点小说基础信息: ${url}`);

    await this.initBrowser();
    const page = await this.browser!.newPage();

    try {
      // 设置页面
      await this.setupPage(page);

      // 导航到页面并等待加载
      await this.navigateToPage(page, url);

      // 检测反爬虫措施
      if (await PageUtils.detectAntiCrawler(page)) {
        this.logger.warn('检测到反爬虫措施，正在处理...');
        await PageUtils.handleAntiCrawler(page);
      }

      // 提取小说基本信息
      const novelData = await this.extractNovelBasicInfo(page);
      this.logger.log(
        `小说基础信息提取完成: ${novelData.title} - ${novelData.author}`,
      );

      // 提取章节列表（不含内容）
      const chapters = await this.extractChapterListOnly(page);
      this.logger.log(`章节列表提取完成，共${chapters.length}章`);

      // 保存基础信息到数据库（不含章节内容）
      const savedNovel = await this.saveBasicInfoToDatabase(
        novelData,
        chapters,
        url,
      );

      const duration = Date.now() - startTime;
      this.logger.log(`小说基础信息解析完成，耗时: ${duration}ms`);

      // 返回基础信息（不含章节内容）
      return this.formatBasicInfoResponse(savedNovel, chapters);
    } catch (error) {
      this.logger.error(`解析小说基础信息失败: ${url}`, error);
      throw error;
    } finally {
      await page.close();
    }
  }

  /**
   * 解析小说链接并直接存储到数据库 - 优化版本
   */
  async parseAndSave(url: string): Promise<any> {
    const startTime = Date.now();
    this.logger.log(`开始解析起点小说: ${url}`);

    await this.initBrowser();
    const page = await this.browser!.newPage();

    try {
      // 设置页面
      await this.setupPage(page);

      // 导航到页面并等待加载
      await this.navigateToPage(page, url);

      // 检测反爬虫措施
      if (await PageUtils.detectAntiCrawler(page)) {
        this.logger.warn('检测到反爬虫措施，正在处理...');
        await PageUtils.handleAntiCrawler(page);
      }

      // 提取小说基本信息
      const novelData = await this.extractNovelBasicInfo(page);
      this.logger.log(
        `小说信息提取完成: ${novelData.title} - ${novelData.author}`,
      );

      // 提取章节列表
      const chapters = await this.extractChapterList(page);
      this.logger.log(`章节列表提取完成，共${chapters.length}章`);

      // 存储到数据库
      const savedNovel = await this.saveToDatabase(novelData, chapters, url);

      const duration = Date.now() - startTime;
      this.logger.log(`小说解析完成，耗时: ${duration}ms`);

      // 返回给前端的数据（不包含章节内容）
      return this.formatResponseData(savedNovel, chapters);
    } catch (error) {
      this.logger.error(`解析小说失败: ${url}`, error);
      throw error;
    } finally {
      await page.close();
    }
  }

  /**
   * 提取小说基本信息 - 优化版本，使用工具类
   */
  private async extractNovelBasicInfo(
    page: puppeteer.Page,
  ): Promise<ParsedNovelData> {
    const result = await PageUtils.safeEvaluate(
      page,
      () => {
        // 内联文本处理工具函数 - 浏览器兼容版本
        const safeTextContent = (element: Element | null): string => {
          return element?.textContent?.trim() || '';
        };

        const safeAttribute = (
          element: Element | null,
          attr: string,
        ): string => {
          return element?.getAttribute(attr) || '';
        };

        const cleanAuthorName = (authorText: string): string => {
          return authorText.replace(/^作者[:：]\s*/, '').trim();
        };

        const cleanUpdateTime = (updateTimeText: string): string => {
          return updateTimeText.replace(/^更新时间[:：]\s*/, '').trim();
        };

        const extractWordCount = (countText: string): number => {
          const numberMatch = countText.match(/([\d.]+).*?万?/);
          if (!numberMatch) return 0;
          const number = parseFloat(numberMatch[1]);
          return countText.includes('万')
            ? Math.floor(number * 10000)
            : Math.floor(number);
        };

        const cleanDescription = (description: string): string => {
          return description
            .replace(/^内容简介[:：]\s*/, '')
            .replace(/\s+/g, ' ')
            .trim();
        };

        const joinTags = (tags: string[]): string => {
          return tags.join('||');
        };

        const bookInfo = document.querySelector('.book-information-normal');
        if (!bookInfo) {
          throw new Error('未找到 book-information-normal 元素');
        }

        // 提取基础文本内容
        const title = safeTextContent(bookInfo.querySelector('#bookName'));
        const authorText = safeTextContent(bookInfo.querySelector('.author'));
        const updateTimeText = safeTextContent(
          bookInfo.querySelector('.update-time'),
        );
        const statusText = safeTextContent(
          bookInfo.querySelector('.book-attribute span'),
        );
        const categoryText = safeTextContent(
          bookInfo.querySelector('.book-attribute a'),
        );
        const countText = safeTextContent(bookInfo.querySelector('.count em'));

        // 处理文本数据
        const author = cleanAuthorName(authorText);
        const updateTime = cleanUpdateTime(updateTimeText);
        const wordCount = extractWordCount(countText);

        // 提取简介和标签
        const introLabel = document.querySelector('.intro-honor-label');
        let description = '';
        let tags = '';

        if (introLabel) {
          const descElement = introLabel.querySelector('#book-intro-detail');
          description = cleanDescription(safeTextContent(descElement));
          const tagElements = introLabel.querySelectorAll(
            '.all-label .gray-hover',
          );
          const tagArray = Array.from(tagElements)
            .map((tag) => safeTextContent(tag))
            .filter(Boolean);
          tags = joinTags(tagArray);
        }

        // 封面图片
        const cover = safeAttribute(bookInfo.querySelector('img'), 'src');

        return {
          title,
          author,
          updateTime,
          status: statusText,
          category: categoryText,
          wordCount,
          description,
          tags,
          cover,
        };
      },
      '提取小说基本信息失败',
    );

    if (!result) {
      throw new Error('提取小说基本信息失败');
    }

    return result;
  }

  /**
   * 仅提取章节列表（不含内容）- 快速方法
   */
  private async extractChapterListOnly(
    page: puppeteer.Page,
  ): Promise<ParsedChapterData[]> {
    try {
      // 等待目录加载
      const catalogElement = await PageUtils.waitForSelector(
        page,
        '#allCatalog',
        10000,
      );
      if (!catalogElement) {
        this.logger.warn('未找到章节目录 #allCatalog');
        return [];
      }

      const chapters = await PageUtils.safeEvaluate(
        page,
        () => {
          // 内联文本处理工具函数 - 浏览器兼容版本
          const safeTextContent = (element: Element | null): string => {
            return element?.textContent?.trim() || '';
          };

          const safeAttribute = (
            element: Element | null,
            attr: string,
          ): string => {
            return element?.getAttribute(attr) || '';
          };

          const catalogEl = document.querySelector('#allCatalog');
          if (!catalogEl) return [];

          const chapterItems = catalogEl.querySelectorAll('.chapter-item');
          const chapters: any[] = [];

          chapterItems.forEach((item) => {
            const chapterNameElement = item.querySelector('.chapter-name');
            const chapterTitle = safeTextContent(chapterNameElement);
            const chapterUrl = safeAttribute(chapterNameElement, 'href');

            // 是否收费检测
            const isLocked = !!item.querySelector('.chapter-locked');
            const isFree = isLocked ? 0 : 1;

            if (chapterTitle && chapterUrl) {
              chapters.push({
                title: chapterTitle,
                isFree: isFree,
                url: chapterUrl,
                content: '', // 不获取内容
                index: chapters.length + 1,
                volumeName: '正文',
              });
            }
          });

          return chapters;
        },
        '提取章节列表失败',
      );

      if (!chapters || chapters.length === 0) {
        this.logger.warn('未找到任何章节');
        return [];
      }

      this.logger.log(`找到${chapters.length}个章节（不含内容）`);
      return chapters;
    } catch (error) {
      this.logger.error('提取章节列表失败:', error);
      return [];
    }
  }

  /**
   * 提取章节列表 - 优化版本，支持批量处理
   */
  private async extractChapterList(
    page: puppeteer.Page,
  ): Promise<ParsedChapterData[]> {
    try {
      // 等待目录加载
      const catalogElement = await PageUtils.waitForSelector(
        page,
        '#allCatalog',
        10000,
      );
      if (!catalogElement) {
        this.logger.warn('未找到章节目录 #allCatalog');
        return [];
      }

      const chapters = await PageUtils.safeEvaluate(
        page,
        () => {
          // 内联文本处理工具函数 - 浏览器兼容版本
          const safeTextContent = (element: Element | null): string => {
            return element?.textContent?.trim() || '';
          };

          const safeAttribute = (
            element: Element | null,
            attr: string,
          ): string => {
            return element?.getAttribute(attr) || '';
          };

          const catalogEl = document.querySelector('#allCatalog');
          if (!catalogEl) return [];

          const chapterItems = catalogEl.querySelectorAll('.chapter-item');
          const chapters: any[] = [];

          chapterItems.forEach((item) => {
            const chapterNameElement = item.querySelector('.chapter-name');
            const chapterTitle = safeTextContent(chapterNameElement);
            const chapterUrl = safeAttribute(chapterNameElement, 'href');

            // 是否收费检测
            const isLocked = !!item.querySelector('.chapter-locked');
            const isFree = isLocked ? 0 : 1;

            if (chapterTitle && chapterUrl) {
              chapters.push({
                title: chapterTitle,
                isFree: isFree,
                url: chapterUrl,
                content: '',
                index: chapters.length + 1,
                volumeName: '正文',
              });
            }
          });

          return chapters;
        },
        '提取章节列表失败',
      );

      if (!chapters || chapters.length === 0) {
        this.logger.warn('未找到任何章节');
        return [];
      }

      this.logger.log(`找到${chapters.length}个章节，开始获取章节内容...`);

      // 使用批量操作获取章节内容（限制数量以提高性能）
      const chaptersToFetch = chapters.slice(0, this.config.maxContentChapters);
      const contentChapters =
        await this.getChapterContentsBatch(chaptersToFetch);

      // 合并数据
      const result = chapters.map((chapter) => {
        const withContent = contentChapters.find((c) => c.url === chapter.url);
        return withContent || { ...chapter, content: '' };
      });

      this.logger.log(`章节内容获取完成，共${result.length}章`);
      return result;
    } catch (error) {
      this.logger.error('提取章节列表失败:', error);
      return [];
    }
  }

  /**
   * 批量获取章节内容 - 优化版本
   */
  private async getChapterContentsBatch(
    chapters: ParsedChapterData[],
  ): Promise<ParsedChapterData[]> {
    return await PageUtils.batchOperation(
      chapters,
      async (chapter: ParsedChapterData) => {
        const content = await this.getSingleChapterContent(chapter.url);
        return { ...chapter, content, wordCount: content.length };
      },
      {
        batchSize: 1, // 减少批量大小，降低并发
        delayBetweenBatches: 8000, // 增加批次间延迟到8秒
        maxRetries: 3,
        retryDelay: 5000,
      },
    );
  }

  /**
   * 获取单个章节内容 - 优化版本
   */
  async getSingleChapterContent(chapterUrl: string): Promise<string> {
    if (!this.browser) {
      await this.initBrowser();
    }

    const page = await this.browser!.newPage();

    try {
      // 使用随机User-Agent和设置
      await this.setupPage(page);

      // 随机延迟，避免被检测为机器人
      await this.randomDelay(3000, 8000);

      // 确保URL有正确的协议前缀
      const fullUrl = chapterUrl.startsWith('http')
        ? chapterUrl
        : `https:${chapterUrl}`;
      //如果 chapterUrl 最后是 / ，则去掉
      if (chapterUrl.endsWith('/')) {
        chapterUrl = chapterUrl.slice(0, -1);
      }
      const chapterId = chapterUrl.split('/').pop() || '';

      // 增加重试机制
      let content = '';
      let retries = 0;
      const maxRetries = 3;

      while (retries < maxRetries && !content) {
        try {
          await page.goto(fullUrl, {
            waitUntil: 'networkidle2',
            timeout: 45000, // 增加超时时间
          });

          // 等待页面完全加载
          await PageUtils.waitForPageLoad(page, { waitTime: 3000 });

          // 检查是否触发了反爬虫机制
          const isBlocked = await page.evaluate(() => {
            // 检查页面是否包含反爬虫标识
            const blockedIndicators = [
              document.title.includes('验证'),
              document.title.includes('404'),
              document.title.includes('错误'),
              document.body.innerText.includes('访问频率'),
              document.body.innerText.includes('请稍后'),
              document.body.innerText.includes('验证码'),
              document.body.innerText.length < 100,
            ];
            return blockedIndicators.some((indicator) => indicator);
          });

          if (isBlocked) {
            this.logger.warn(
              `检测到反爬虫机制，触发重试: ${chapterUrl}, 重试次数: ${retries + 1}`,
            );
            retries++;

            if (retries < maxRetries) {
              // 长时间延迟后重试
              await this.randomDelay(10000, 20000);
              continue;
            } else {
              this.logger.error(
                `反爬虫机制触发，已达最大重试次数: ${chapterUrl}`,
              );
              return '';
            }
          }

          const result = await PageUtils.safeEvaluateWithArgs(
            page,
            (currentChapterId: string) => {
              // 内联文本处理工具函数 - 浏览器兼容版本
              const safeTextContent = (element: Element | null): string => {
                return element?.textContent?.trim() || '';
              };

              // 多种选择器尝试
              const selectors = [
                '#c-' + currentChapterId,
                '.chapter-content',
                '.content',
                '.text-content',
                '[class*="content"]',
              ];

              let chapterWrapper: Element | null = null;
              for (const selector of selectors) {
                chapterWrapper = document.querySelector(selector);
                if (chapterWrapper) break;
              }

              if (!chapterWrapper) {
                // 记录页面结构用于调试
                console.log('页面结构调试:', {
                  bodyText: document.body.innerText.substring(0, 200),
                  title: document.title,
                  chapterId: currentChapterId,
                });
                return '';
              }

              let content = safeTextContent(chapterWrapper);

              // 清理内容，移除无用信息
              content = content
                .replace(/^[\s\n]*[一二三四五六七八九十百千万]+、[\s\n]*/, '') // 去除章节标题
                .replace(/[请记住本书首发域名.*?帮助宣传本书]/g, '') // 去除推广信息
                .replace(/[笔趣库.*?最新章节全文阅读]/g, '') // 去除网站信息
                .replace(/.*?天才一秒记住.*?更新快！.*$/g, '') // 去除缓存提示
                .replace(/\s+/g, ' ') // 标准化空白字符
                .trim();

              return content;
            },
            [chapterId],
            '获取章节内容失败',
          );

          content = result || '';

          // 如果获取到的内容太短，可能是反爬虫
          if (content && content.length < 100) {
            this.logger.warn(
              `章节内容过短，可能触发反爬虫: ${chapterUrl}, 内容长度: ${content.length}`,
            );
            content = '';
            retries++;
            if (retries < maxRetries) {
              await this.randomDelay(8000, 15000);
            }
          }
        } catch (error) {
          this.logger.warn(
            `获取章节内容异常，重试中: ${chapterUrl}, 错误: ${error.message}`,
          );
          retries++;
          if (retries < maxRetries) {
            await this.randomDelay(5000, 12000);
          }
        }
      }

      this.logger.log(
        `章节内容获取完成: ${chapterUrl}, 字数: ${content.length}`,
      );
      return content;
    } catch (error) {
      this.logger.error(`获取章节内容失败: ${chapterUrl}`, error);
      return '';
    } finally {
      await page.close();
    }
  }

  /**
   * 仅保存基础信息到数据库（不含章节内容）- 快速方法
   */
  private async saveBasicInfoToDatabase(
    novelData: ParsedNovelData,
    chapters: ParsedChapterData[],
    originalUrl: string,
  ): Promise<Novel> {
    const queryRunner = this.dataSource.createQueryRunner();
    await queryRunner.connect();
    await queryRunner.startTransaction();

    try {
      // 处理分类
      const category = await this.findOrCreateCategory(
        queryRunner.manager,
        novelData.category,
      );

      // 创建或更新小说
      const novel = await this.findOrCreateNovel(
        queryRunner.manager,
        novelData,
        category,
        originalUrl,
      );

      // 仅保存章节基本信息（不含内容）
      await this.saveChapterBasicInfo(queryRunner.manager, novel.id, chapters);

      await queryRunner.commitTransaction();
      this.logger.log(
        `✅ 成功保存小说基础信息: ${novelData.title}, 章节数: ${chapters.length}`,
      );

      return novel;
    } catch (error) {
      await queryRunner.rollbackTransaction();
      this.logger.error('❌ 保存基础信息到数据库失败:', error);
      throw error;
    } finally {
      await queryRunner.release();
    }
  }

  /**
   * 保存到数据库 - 优化版本
   */
  private async saveToDatabase(
    novelData: ParsedNovelData,
    chapters: ParsedChapterData[],
    originalUrl: string,
  ): Promise<Novel> {
    const queryRunner = this.dataSource.createQueryRunner();
    await queryRunner.connect();
    await queryRunner.startTransaction();

    try {
      // 处理分类
      const category = await this.findOrCreateCategory(
        queryRunner.manager,
        novelData.category,
      );

      // 创建或更新小说
      const novel = await this.findOrCreateNovel(
        queryRunner.manager,
        novelData,
        category,
        originalUrl,
      );

      // 批量保存章节
      await this.saveChapters(queryRunner.manager, novel.id, chapters);

      await queryRunner.commitTransaction();
      this.logger.log(
        `✅ 成功保存小说: ${novelData.title}, 章节数: ${chapters.length}`,
      );

      return novel;
    } catch (error) {
      await queryRunner.rollbackTransaction();
      this.logger.error('❌ 保存到数据库失败:', error);
      throw error;
    } finally {
      await queryRunner.release();
    }
  }

  /**
   * 查找母分类（使用分类映射系统）
   */
  private async findOrCreateCategory(
    manager: any,
    categoryName: string,
  ): Promise<MasterCategory | null> {
    if (!categoryName) return null;

    try {
      // 使用分类映射服务映射起点分类到母分类
      const masterCategory = await this.categoryMappingService.mapSiteCategoryToMaster('qidian', categoryName);

      if (masterCategory) {
        this.logger.log(`✅ 分类映射成功: ${categoryName} -> ${masterCategory.name}`);
        return masterCategory;
      } else {
        // 如果没有找到映射，使用默认分类
        this.logger.warn(`⚠️  未找到分类映射: ${categoryName}，使用默认分类`);
        return await this.createDefaultCategory(manager, categoryName);
      }
    } catch (error) {
      this.logger.error(`❌ 分类映射处理失败: ${categoryName}`, error);
      // 出错时使用默认分类
      return await this.createDefaultCategory(manager, categoryName);
    }
  }

  /**
   * 创建默认母分类（回退方案）
   */
  private async createDefaultCategory(
    manager: any,
    categoryName: string,
  ): Promise<MasterCategory | null> {
    // 尝试使用"都市"作为默认分类
    let masterCategory = await manager.findOne(MasterCategory, {
      where: { name: '都市' },
    });

    if (!masterCategory) {
      // 如果没有都市分类，查找第一个可用的母分类
      masterCategory = await manager.findOne(MasterCategory, {
        where: { status: 1 },
        order: { sortOrder: 'ASC' },
      });
    }

    if (!masterCategory) {
      this.logger.error(`❌ 系统中没有可用的母分类，无法创建小说`);
      return null;
    }

    this.logger.log(`✅ 使用默认母分类: ${masterCategory.name}`);
    return masterCategory;
  }

  /**
   * 查找或创建小说
   */
  private async findOrCreateNovel(
    manager: any,
    novelData: ParsedNovelData,
    masterCategory: MasterCategory | null,
    originalUrl: string,
  ): Promise<Novel> {
    let novel = await manager.findOne(Novel, {
      where: { title: novelData.title, author: novelData.author },
    });

    if (!novel) {
      // 创建新小说
      novel = manager.create(Novel, {
        title: novelData.title,
        author: novelData.author,
        description: novelData.description,
        masterCategoryId: masterCategory?.id,
        status: TextUtils.normalizeStatus(novelData.status),
        wordCount: novelData.wordCount,
        tags: novelData.tags ? novelData.tags.split('||').filter(Boolean) : [],
        platform: 'qidian',
        originalUrl: originalUrl,
        coverImage: novelData.cover || '',
      });
      this.logger.log(`✅ 创建新小说: ${novelData.title}`);
    } else {
      // 更新现有小说
      novel.description = novelData.description;
      novel.masterCategoryId = masterCategory?.id;
      novel.wordCount = novelData.wordCount;
      novel.tags = novelData.tags
        ? novelData.tags.split('||').filter(Boolean)
        : novel.tags;
      novel.coverImage = novelData.cover || novel.coverImage;
      this.logger.log(`🔄 更新现有小说: ${novelData.title}`);
    }

    return await manager.save(novel);
  }

  /**
   * 批量保存章节
   */
  private async saveChapters(
    manager: any,
    novelId: number,
    chapters: ParsedChapterData[],
  ): Promise<void> {
    const chapterEntities: NovelChapter[] = [];

    for (let i = 0; i < chapters.length; i++) {
      const chapterData = chapters[i];

      // 检查章节是否已存在
      let chapter = await manager.findOne(NovelChapter, {
        where: {
          novelId: novelId,
          chapterNumber: i + 1,
        },
      });

      if (!chapter) {
        chapter = manager.create(NovelChapter, {
          novelId: novelId,
          title: chapterData.title,
          content: chapterData.content,
          chapterNumber: i + 1,
          wordCount: chapterData.content.length,
          chapterUrl: chapterData.url,
          isFree: chapterData.isFree,
          volumeName: chapterData.volumeName || '正文',
        });
      } else {
        // 更新现有章节
        chapter.title = chapterData.title;
        chapter.content = chapterData.content;
        chapter.wordCount = chapterData.content.length;
        chapter.isFree = chapterData.isFree;
        chapter.volumeName = chapterData.volumeName || chapter.volumeName;
      }

      chapterEntities.push(chapter);
    }

    // 批量保存
    await manager.save(chapterEntities);
    this.logger.log(`✅ 保存${chapterEntities.length}个章节`);
  }

  /**
   * 仅保存章节基本信息（不含内容）- 快速方法
   */
  private async saveChapterBasicInfo(
    manager: any,
    novelId: number,
    chapters: ParsedChapterData[],
  ): Promise<void> {
    const chapterEntities: NovelChapter[] = [];

    for (let i = 0; i < chapters.length; i++) {
      const chapterData = chapters[i];

      // 检查章节是否已存在
      let chapter = await manager.findOne(NovelChapter, {
        where: {
          novelId: novelId,
          chapterNumber: i + 1,
        },
      });

      if (!chapter) {
        chapter = manager.create(NovelChapter, {
          novelId: novelId,
          title: chapterData.title,
          content: '', // 内容为空，后续异步填充
          chapterNumber: i + 1,
          wordCount: 0, // 暂时为0
          chapterUrl: chapterData.url,
          isFree: chapterData.isFree,
          volumeName: chapterData.volumeName || '正文',
        });
      } else {
        // 更新现有章节的基本信息（不覆盖内容）
        chapter.title = chapterData.title;
        chapter.chapterUrl = chapterData.url;
        chapter.isFree = chapterData.isFree;
        chapter.volumeName = chapterData.volumeName || chapter.volumeName;
        // 不更新content和wordCount，保留现有内容
      }

      chapterEntities.push(chapter);
    }

    // 批量保存
    await manager.save(chapterEntities);
    this.logger.log(`✅ 保存${chapterEntities.length}个章节基本信息`);
  }

  /**
   * 设置页面
   */
  private async setupPage(page: puppeteer.Page): Promise<void> {
    // 使用随机User-Agent
    await page.setUserAgent(this.getRandomUserAgent());
    await page.setViewport({
      width: 1920 + Math.floor(Math.random() * 100),
      height: 1080 + Math.floor(Math.random() * 100),
    });

    // 反检测设置
    await page.evaluateOnNewDocument(() => {
      // 移除webdriver标识
      Object.defineProperty(navigator, 'webdriver', {
        get: () => undefined,
      });

      // 伪造plugins
      Object.defineProperty(navigator, 'plugins', {
        get: () => [
          {
            0: {
              type: 'application/x-google-chrome-pdf',
              suffixes: 'pdf',
              description: 'Portable Document Format',
            },
            description: 'Portable Document Format',
            filename: 'internal-pdf-viewer',
            length: 1,
            name: 'Chrome PDF Plugin',
          },
          {
            0: {
              type: 'application/pdf',
              suffixes: 'pdf',
              description: 'Portable Document Format',
            },
            description: 'Portable Document Format',
            filename: 'mhjfbmdgcfjbbpaeojofohoefgiehjai',
            length: 1,
            name: 'Chrome PDF Viewer',
          },
        ],
      });

      // 伪造languages
      Object.defineProperty(navigator, 'languages', {
        get: () => ['zh-CN', 'zh', 'en-US', 'en'],
      });

      // 随机化一些浏览器特征
      Object.defineProperty(navigator, 'hardwareConcurrency', {
        get: () => 4 + Math.floor(Math.random() * 8),
      });

      Object.defineProperty(navigator, 'deviceMemory', {
        get: () => 4 + Math.floor(Math.random() * 4),
      });

      // 伪造Chrome运行时
      Object.defineProperty(window, 'chrome', {
        get: () => ({
          runtime: {},
          loadTimes: function () {},
          csi: function () {},
          app: {},
        }),
      });

      // 伪造permissions
      const originalQuery = window.navigator.permissions.query;
      window.navigator.permissions.query = (parameters) =>
        parameters.name === 'notifications'
          ? Promise.resolve({ state: Notification.permission } as any)
          : originalQuery(parameters);

      // 伪造屏幕分辨率
      Object.defineProperty(screen, 'availWidth', {
        get: () => 1920,
      });
      Object.defineProperty(screen, 'availHeight', {
        get: () => 1080,
      });
      Object.defineProperty(screen, 'width', {
        get: () => 1920,
      });
      Object.defineProperty(screen, 'height', {
        get: () => 1080,
      });
    });
  }

  /**
   * 导航到页面并等待加载
   */
  private async navigateToPage(
    page: puppeteer.Page,
    url: string,
  ): Promise<void> {
    await page.goto(url, {
      waitUntil: 'networkidle2',
      timeout: 30000,
    });

    await PageUtils.waitForPageLoad(page, {
      waitTime: 2000,
      maxWaitTime: 15000,
      waitCondition: async () => {
        return await page.evaluate(() => {
          return !!document.querySelector(
            '.book-information-normal, #allCatalog',
          );
        });
      },
    });
  }

  /**
   * 格式化响应数据
   */
  private formatResponseData(novel: Novel, chapters: ParsedChapterData[]): any {
    return {
      id: novel.id,
      title: novel.title,
      author: novel.author,
      description: novel.description,
      category: novel.masterCategory?.name,
      tags: novel.tags,
      coverImage: novel.coverImage,
      status: novel.status,
      wordCount: novel.wordCount,
      platform: novel.platform,
      originalUrl: novel.originalUrl,
      chapters: chapters.map((chapter, index) => ({
        title: chapter.title,
        chapterNumber: index + 1,
        isFree: chapter.isFree,
        volumeName: chapter.volumeName || '正文',
        wordCount: chapter.content?.length || 0,
        hasContent: !!chapter.content,
      })),
    };
  }

  /**
   * 格式化基础信息响应数据（不含章节内容）- 快速方法
   */
  private formatBasicInfoResponse(
    novel: Novel,
    chapters: ParsedChapterData[],
  ): any {
    return {
      id: novel.id,
      title: novel.title,
      author: novel.author,
      description: novel.description,
      category: novel.masterCategory?.name,
      tags: novel.tags,
      coverImage: novel.coverImage,
      status: novel.status,
      wordCount: novel.wordCount,
      platform: novel.platform,
      originalUrl: novel.originalUrl,
      chapters: chapters.map((chapter, index) => ({
        title: chapter.title,
        chapterNumber: index + 1,
        isFree: chapter.isFree,
        volumeName: chapter.volumeName || '正文',
        wordCount: 0, // 内容待异步获取
        hasContent: false, // 标记内容待获取
      })),
    };
  }

  
  /**
   * 检查URL是否为起点域名
   */
  isSupportedUrl(url: string): boolean {
    return url.includes('qidian.com') || url.includes('yuewen.com');
  }
}
